//-----------------------------------------------------------------------------
// Bend2D - Physics Engine
// Roberto Bender <beto.bender@gmail.com>
// Description: 
// Date: 17/05/2010
//-----------------------------------------------------------------------------
#include "extension/colors.h"
#include "bmath.h"
#include <iostream>
#include <sstream>
using namespace bend2d;
using namespace std;

static unsigned char clip(double value)
{
    if(value > 255) return 255;
    if(value < 0) return 0;
    return (unsigned char) value;
}

Color::Color() :
    m_Red(0),
    m_Green(0),
    m_Blue(0),
    m_Alpha(0xFF)
{}

Color::Color(unsigned int color, eColorSourceType type)
{
    switch(type)
    {
        case eSourceType8888:
            m_Red   = (unsigned char)(color >> 24);
            m_Green = (unsigned char)(color >> 16);
            m_Blue  = (unsigned char)(color >> 8);
            m_Alpha = (unsigned char)(color);
            break;
        case eSourceType565:
            m_Red   = ((color >> 11) & 31) * (255/31);
            m_Green = ((color >> 5) & 63) * (255/63);
            m_Blue  =  (color & 31) * (255/31);
            m_Alpha = 0xFF;
            break;
        case eSourceTypeYCbCr:
            {
                unsigned char Y = color >> 24;
                unsigned char U = color >> 16;
                unsigned char V = color >> 8;
                m_Alpha = 0xFF;

                m_Red = clip(Y + 1.4075 * (V - 128));
                m_Green = clip(Y - 0.3455 * (U - 128) - (0.7169 * (V - 128)));
                m_Blue = clip((Y + 1.7790 * (U - 128)));
           }
           break;
        default:
            m_Red = m_Green = m_Blue = m_Alpha = 0xFF;
            break;
    }
}

Color::Color(unsigned char r, unsigned char g, unsigned char b, unsigned char a) :
    m_Red(r),
    m_Green(g),
    m_Blue(b),
    m_Alpha(a)
{
}

unsigned short Color::To565(eColorOutputMask mask) const
{
    unsigned short retvalue = 0;
    retvalue = (m_Red >> 3);
    retvalue = retvalue << 6 | (m_Green >> 2);
    retvalue = retvalue << 5 | (m_Blue >> 3);
    return retvalue & mask;
}

unsigned int Color::To8888(eColorOutputMask mask) const
{
    unsigned int retvalue = 0;
    retvalue = m_Red;
    retvalue <<= 8;
    retvalue |= m_Green;
    retvalue <<= 8;
    retvalue |= m_Blue;
    retvalue <<= 8;
    retvalue |= m_Alpha;
    return (retvalue & mask);
}

void Color::ToGLFloat(float& r, float& g, float& b, float& a) const
{
    r = (float)(m_Red / 255.0);
    g = (float)(m_Green / 255.0);
    b = (float)(m_Blue / 255.0);
    a = (float)(m_Alpha / 255.0);
}

unsigned char Color::GetRed() const
{
    return m_Red;
}

unsigned char Color::GetBlue() const
{
    return m_Blue;
}

unsigned char Color::GetGreen() const
{
    return m_Green;
}

unsigned char Color::GetAlpha() const
{
    return m_Alpha;
}

void Color::SetAlpha(unsigned char alpha)
{
    m_Alpha = alpha;
}

bool Color::Match(const Color& color, unsigned char error) const
{
   double d = Math::Sqrt(Math::Pow(m_Red-color.m_Red, 2) + Math::Pow(m_Green-color.m_Green, 2) + Math::Pow(m_Blue-color.m_Blue, 2));
    return d <= error;
}

void Color::Normalize()
{
#if 0
    if(m_Red > m_Green && m_Red > m_Blue)
    {
        m_Red = (m_Red / 17) * 17;
        m_Green = 0;
        m_Blue = 0;
    }
    else if(m_Green > m_Blue)
    {
        m_Green = (m_Green / 17) * 17;
        m_Blue = 0;
        m_Red = 0;
    }
    else
    {
        m_Blue = (m_Blue / 17) * 17;
        m_Red = 0;
        m_Green = 0;
    }
#endif

    if(m_Red < 128)
    {
        m_Red = 0;
        m_Green = 255;
        m_Blue = 255;
    }
    else if(m_Green < 128)
    {
        m_Red = 255;
        m_Green = 0;
        m_Blue = 255;
    }
    else if(m_Blue < 128)
    {
        m_Red = 255;
        m_Green = 255;
        m_Blue = 0;
    }


}

void Color::NormalizeColors(unsigned char number)
{
    if(number == 0)
        return;

    m_Red = (m_Red / number) * number;
    m_Blue = (m_Blue / number) * number;
    m_Green = (m_Green / number) * number;
}

bool Color::operator == (const Color& right) const
{
    return m_Red == right.m_Red &&
           m_Blue == right.m_Blue &&
           m_Green == right.m_Green &&
           m_Alpha == right.m_Alpha;
}

bool Color::IsTheHighestComponent(eColorComponent component) const
{
    switch(component)
    {
        case eColorComponentRed:
            return m_Red >= m_Green && m_Red >= m_Blue;
        case eColorComponentGreen:
            return m_Green >= m_Red && m_Green >= m_Blue;
        case eColorComponentBlue:
            return m_Blue >= m_Red && m_Blue >= m_Green;
        default:
            return false;
    }
}
